package test.resource

import cn.hutool.core.text.StrPool
import cn.hutool.log.Log
import com.gitee.ixtf.core.EntityDTO
import com.gitee.ixtf.vertx.ZipOutputStreamState
import io.minio.GetObjectArgs
import io.minio.ListObjectsArgs
import io.minio.MinioAsyncClient
import io.minio.MinioClient
import io.opentelemetry.api.trace.Span
import io.vertx.core.Vertx
import io.vertx.core.eventbus.EventBus
import io.vertx.core.file.FileSystem
import io.vertx.core.http.HttpServerResponse
import io.vertx.core.impl.VertxInternal
import io.vertx.ext.web.FileUpload
import io.vertx.ext.web.RoutingContext
import io.vertx.kotlin.core.json.jsonObjectOf
import io.vertx.kotlin.coroutines.coAwait
import jakarta.annotation.security.PermitAll
import jakarta.inject.Inject
import jakarta.validation.Valid
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotEmpty
import jakarta.validation.constraints.NotNull
import jakarta.ws.rs.*
import jakarta.ws.rs.core.MediaType
import java.io.InputStream
import java.util.concurrent.TimeUnit
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.reactor.awaitSingle
import kotlinx.coroutines.reactor.mono
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import test.verticle.COUNT
import test.verticle.TEST_ADDRESS

data class TestPostCommand(
    @field:NotBlank val id: String,
    @field:NotBlank val name: String,
    @field:NotEmpty val stringSet: Set<@Valid @NotBlank String>,
)

val minioAsyncClient: MinioAsyncClient by lazy {
  MinioAsyncClient.builder()
      .endpoint("https://play.min.io")
      .credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
      .build()
}

val minioClient: MinioClient by lazy {
  MinioClient.builder()
      .endpoint("http://172.10.100.112:9000")
      .credentials("minio", "medipath888")
      .build()
}

@Path("")
class RestfulResource
@Inject
constructor(
    private val vertx: Vertx,
    private val eventBus: EventBus,
    private val fileSystem: FileSystem,
) {
  @PermitAll
  @Path("TifTileBackward/{b}/{p}/{l}/{x}/{y}")
  suspend fun block(
      @PathParam("b") b: String,
      @PathParam("p") p: String,
      @PathParam("l") l: Int,
      @PathParam("x") x: Int,
      @PathParam("y") y: Int,
  ) = "minio://${b}/${p}/${l}/${x}/${y}"

  @Path("block")
  suspend fun block(): String {
    Log.get().warn("block: {}", Span.current())
    delay(10.seconds)
    return "block"
  }

  @Path("TestGet")
  suspend fun TestGet(rc: RoutingContext) = runBlocking {
    Log.get().warn("TestGet")
    delay(3.seconds)
    // TimeUnit.SECONDS.sleep(3)
    "TestGet"
  }

  @Produces(MediaType.TEXT_PLAIN)
  @Path("TestCollection")
  suspend fun restful(its: Collection<EntityDTO>) {
    val tracer = (vertx as VertxInternal).tracer()
    Log.get().warn("block: {} {}", tracer, Span.current())
    mono { its.map { it.id } }.awaitSingle()
  }

  @Path("TestUl")
  suspend fun TestUl(@NotEmpty fileUploads: Collection<@Valid @NotNull FileUpload>): List<String> {
    Log.get().warn("TestUl")
    withContext(Dispatchers.IO) { TimeUnit.SECONDS.sleep(3) }
    return mono { fileUploads.map { it.fileName() } }.awaitSingle()
  }

  @Path("TestSuspend")
  suspend fun TestSuspend(@QueryParam("u") u: String?) = mono { "TestSuspend" }.awaitSingle()

  @Path("TestPost") fun TestPost(rc: RoutingContext, command: TestPostCommand) = "TestPost"

  @Produces(MediaType.APPLICATION_OCTET_STREAM)
  @Path("TestQueue")
  suspend fun restful() =
      eventBus.request<Boolean>(TEST_ADDRESS, "count-${COUNT.incrementAndGet()}").coAwait()

  @Produces(MediaType.APPLICATION_OCTET_STREAM)
  @Path("/dl/minio")
  fun dl(rc: RoutingContext) =
      GetObjectArgs.builder().run {
        bucket("public")
        `object`("myTest.png")
        minioClient.getObject(build())
      }

  @Produces(MediaType.APPLICATION_OCTET_STREAM)
  @Path("/dl/minioZip")
  fun dlZip(response: HttpServerResponse): ZipOutputStreamState {
    val bucket = "test"
    val prefix = ""
    return ZipOutputStreamState {
      ListObjectsArgs.builder()
          .run {
            bucket(bucket)
            prefix(prefix)
            delimiter(StrPool.SLASH)
            recursive(true)
            minioClient.listObjects(build())
          }
          .map {
            val item = it.get()
            item.objectName().removePrefix(prefix) to
                suspend {
                  GetObjectArgs.builder().run {
                    bucket(bucket)
                    `object`(item.objectName())
                    minioClient.getObject(build())
                  } as InputStream
                }
          }
    }
  }

  @Path("/dl/tokens/{token}") fun dl(rc: RoutingContext, @PathParam("token") token: String) = token

  @Path("/Tile/{path}/{l}/{x}/{y}")
  fun dl(
      rc: RoutingContext,
      @PathParam("path") path: String,
      @PathParam("l") l: Int,
      @PathParam("x") x: Int,
      @PathParam("y") y: Int
  ) = jsonObjectOf("path" to path, "l" to l, "x" to x, "y" to y)

  @Path("/ul/test/{token}")
  fun test2(rc: RoutingContext, @FormParam("token") token: List<FileUpload>) = token
}
